#include "plantclient.h"
#include "valueparser.h"
#include <cstdlib>
#include <iostream>
#include <netdb.h>
#include <cstring>
#include <sstream>

using namespace std;

PlantClient::PlantClient(Plant* plant)
  : port(4444), plant(plant)
{
  strcpy(hostname, "127.0.0.1");
}

bool PlantClient::init(const char *fname)
{	
  adresse.sin_family = AF_INET;
  adresse.sin_port = htons(port); // please specify the port number here
  inet_aton(hostname,&ip);     // please specify the IP-adress of the server
  server = gethostbyaddr((char*) &ip,sizeof(ip),AF_INET);
  if (server == 0) {
    cout << "client: wrong ip-adress of server" << endl;
    return false;
  }
  memcpy(&adresse.sin_addr,server->h_addr_list[0],sizeof(adresse.sin_addr));
  
  if ( (socket_id = socket(PF_INET, SOCK_STREAM,0)) < 0) {	 
    cout << "client: cannot create socket" << endl;
    return false;
  }
  
  // connecting to server
  if (connect(socket_id,(struct sockaddr *) &adresse, sizeof(adresse))) {
    cout << "client: cannot connect to server" << endl;
    return false;
  }
  
  // waiting for initial information from server
  int n;
  int j = 0;
  while( (n = read(socket_id,&information[j],1)) > 0) {
    if (information[j] == '\n') {
      information[j] = '\0';
      break;
    }
    j++;
  }
  
  if (strncmp(information, "Welcome.", 8) != 0) {
    cerr << "ERROR: " << __PRETTY_FUNCTION__ 
         << ": connecting to server failed. Unexpected welcome message."
         << endl;
    return false;
  } else {
    cout << "client: received the following welcome message: "
         << information << endl;
  }
  
  double delta_t = 1.;
  plant->init(x_dim, o_dim, u_dim, delta_t, fname);

  state  = new double[x_dim];
  next_state = new double[x_dim];
  target_state = new double[x_dim];
  action = new double[u_dim];
  observation = new double[o_dim];  
  
  // prepare string for server
  stringstream str;
  str << x_dim << " " << o_dim << " " << u_dim << " " << delta_t << "\n";

  // send acknowledgement to server 
  if (write(socket_id,str.str().c_str(),str.str().length()) <= 0) {
    cout << "client: communication with server failed"  << endl;
    return false;
  }    
  cout << "client: initial information send to server:" << endl;
  cout << "dimension of state space: " << x_dim << endl;
  cout << "dimension of observable state space: " << o_dim << endl; 
  cout << "dimension of action space:" << u_dim << endl << endl; 	
  cout << "delta t" << delta_t << endl << endl; 	  
  
  return true;
}

bool PlantClient::run()
{
  int n;	
  int j = 0;	
  memset(information, '\0', PlantClient_max_com_buf);
  
  while (true) {
    j = 0;

    while( (n = read(socket_id,&information[j],1)) > 0) {
      if (information[j] == '\n') {				
        information[j+1] = '\0';
        break;			
      }
      j++;							
    }

    char p[PlantClient_max_com_buf];    
    strcpy(p,information);
    char* token = strtok(p, " \n");
    if (! token) {
      cerr << "ERROR: " << __PRETTY_FUNCTION__ 
           << ": received message of unexpected type" << endl;
      return false;
    }
    // action ////////////////////////////////////////////////////////////////
    if (strncmp(token, "action", 6) == 0) {			 
      for (int i = 0; i < x_dim;i++) {      
        token = strtok(0, " \n");
        if (!token) {
          cerr << "ERROR: " << __PRETTY_FUNCTION__
               << ": message of type action to short." << endl;
          return false;
        }
        state[i] = atof(token);
      }
      for (int i = 0; i < u_dim;i++) {      
        token = strtok(0, " \n");
        if (!token) {
          cerr << "ERROR: " << __PRETTY_FUNCTION__
               << ": message of type action to short." << endl;
          return false;
        }
        action[i] = atof(token);
      }

      plant->next_state(state, action, next_state); 
      // sending response to server (control signal)
      stringstream response;
      for (int i = 0; i < x_dim; i++) {
        response << next_state[i] << " ";
      }
      response << "\n";
      if (write(socket_id, response.str().c_str(), 
                response.str().length()) < 0) {
        cerr << "communication with server failed" << endl;				
	      return false;
      }
      continue;
    } 
    // initial ///////////////////////////////////////////////////////////////
    else if (strncmp(token, "initial", 7) == 0) {
      // server asks for initial state of sequence
      for (int i = 0; i < x_dim;i++){      
        token = strtok(0, " \n");
        if (!token) {
          cerr << "ERROR: " << __PRETTY_FUNCTION__
               << ": message of type initial to short." << endl;
          return false;
        }
        state[i] = atof(token);
      }
      for (int i = 0; i < x_dim;i++){      
        token = strtok(0, " \n");
        if (!token) {
          cerr << "ERROR: " << __PRETTY_FUNCTION__
               << ": message of type initial to short." << endl;
          return false;
        }
        target_state[i] = atof(token);
      }
      plant->check_initial_state(state, target_state); // TODO: FALSE!!!
      // sending response to server (control signal)
      stringstream response;
      for (int i = 0; i < x_dim; i++) {
        response << state[i] << " ";
      }
      for (int i = 0; i < x_dim; i++) {
        response << target_state[i] << " ";
      }
      response << "\n";
      if (write(socket_id, response.str().c_str(), 
                response.str().length()) < 0) {
        cerr << "communication with server failed" << endl;				
	      return false;
      }
      continue;
    }         
    // observed //////////////////////////////////////////////////////////////
    else if (strncmp(token, "observed", 8) == 0) {
      // server asks for observation
      for (int i = 0; i < x_dim;i++){      
        token = strtok(0, " \n");
        if (!token) {
          cerr << "ERROR: " << __PRETTY_FUNCTION__
               << ": message of type observed to short." << endl;
          return false;
        }
        state[i] = atof(token);
      }
      for (int i = 0; i < x_dim;i++){      
        token = strtok(0, " \n");
        if (!token) {
          cerr << "ERROR: " << __PRETTY_FUNCTION__
               << ": message of type observed to short." << endl;
          return false;
        }
        target_state[i] = atof(token);
      }
      plant->get_observed_state(state, target_state, observation); // TODO: FALSE!!!
      // sending response to server (control signal)
      stringstream response;
      for (int i = 0; i < o_dim; i++) {
        response << observation[i] << " ";
      }
      response << "\n";
      if (write(socket_id, response.str().c_str(), 
                response.str().length()) < 0) {
        cerr << "communication with server failed" << endl;				
	      return false;
      }
      continue;
    }       
    // start /////////////////////////////////////////////////////////////////  
    else if (strncmp(token, "start", 5) == 0) {
      plant->notify_sequence_starts();
      continue;
    }
    // stop //////////////////////////////////////////////////////////////////  
    else if (strncmp(token, "stop", 4) == 0) {
      plant->notify_sequence_stops();
      continue;
    }    
    // exit //////////////////////////////////////////////////////////////////  
    else if (strncmp(token, "exit", 4) == 0) {
      plant->deinit();
      return true;
    }
    else {
      cerr << "ERROR: " << __PRETTY_FUNCTION__
           << ": unknown command type" << endl;
      return false;
    }
  }			
  return true;				
}

bool PlantClient::deinit()
{
  close(socket_id);
  
  delete [] state;
  delete [] target_state;
  delete [] next_state;
  delete [] action;
  delete [] observation;
  
  return true;
}

void PlantClient::read_options(const char* fname) 
{
  if (fname == 0)
    return;
  
  ValueParser vp(fname,"PlantClient");	
  vp.get("port",port);
  vp.get("hostname",hostname, 1000);
}


